/*
 * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
 *
 * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved.
 *
 * Please visit http://javatelnet.org/ for updates and contact.
 *
 * --LICENSE NOTICE--
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * --LICENSE NOTICE--
 *
 */

package de.mud.jta.plugin;

import de.mud.jta.Plugin;
import de.mud.jta.PluginBus;
import de.mud.jta.PluginConfig;
import de.mud.jta.VisualPlugin;
import de.mud.jta.event.ConfigurationListener;
import de.mud.jta.event.ReturnFocusListener;
import de.mud.jta.event.SocketRequest;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.net.URL;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

/**
 * The MudConnector (http://www.mudconnector.com) plugin. The plugin will
 * download a list of MUDs from a special list availabe at the url above and the
 * user can select the mud and connect to it. This usually requires the relayd
 * program to be run on the web server as this plugin tries to establish
 * connections to other hosts than the web server.
 * <P>
 * <B>Maintainer:</B> Matthias L. Jugel
 * 
 * @version $Id: MudConnector.java 499 2005-09-29 08:24:54Z leo $
 * @author Matthias L. Jugel, Marcus Mei�ner
 */
public class MudConnector extends Plugin implements VisualPlugin, Runnable,
	ActionListener
{

    /** debugging level */
    private final static int debug = 0;

    protected URL listURL = null;
    protected int step;
    protected Map mudList = null;
    protected JList mudListSelector = new JList();
    protected JTextField mudName, mudAddr, mudPort;
    protected JButton connect;
    protected JPanel mudListPanel;
    protected CardLayout layouter;
    protected ProgressBar progress;
    protected JLabel errorLabel;
    protected JMenu MCMenu;

    /**
     * Implementation of a progress bar to display the progress of loading the
     * mud list.
     */
    class ProgressBar extends JComponent
    {
	int max, current;
	String text;
	Dimension size = new Dimension(250, 20);
	Image backingStore;

	public void setMax(int max)
	{
	    this.max = max;
	}

	public void update(Graphics g)
	{
	    paint(g);
	}

	public void paint(Graphics g)
	{
	    if (backingStore == null)
	    {
		backingStore = createImage(getSize().width, getSize().height);
		redraw();
	    }
	    g.drawImage(backingStore, 0, 0, this);
	}

	private void redraw()
	{
	    if (backingStore == null || text == null)
		return;
	    Graphics g = backingStore.getGraphics();
	    int width = (int) (((float) current / (float) max) * getSize().width);
	    g.fill3DRect(0, 0, getSize().width, getSize().height, false);
	    g.setColor(getBackground());
	    g.fill3DRect(0, 0, width, getSize().height, true);
	    g.setColor(getForeground());
	    g.setXORMode(getBackground());
	    String percent = "" + (current * 100 / (max > 0 ? max : 1))
		    + "% / " + current + " of " + max;
	    g.drawString(percent, getSize().width / 2
		    - getFontMetrics(getFont()).stringWidth(percent) / 2,
		    getSize().height / 2);
	    g.drawString(text, getSize().width / 2
		    - getFontMetrics(getFont()).stringWidth(text) / 2,
		    getSize().height / 2 + 12);
	    paint(getGraphics());
	}

	public void adjust(int value, String name)
	{
	    if ((current = value) > max)
		current = max;
	    text = name;
	    if (((float) current / (float) step) == (int) (current / step))
		redraw();
	}

	public void setSize(int width, int height)
	{
	    size = new Dimension(width, height);
	}

	public Dimension getPreferredSize()
	{
	    return size;
	}

	public Dimension getMinimumSize()
	{
	    return size;
	}
    }

    /**
     * Create the list plugin and get the url to the actual list.
     */
    public MudConnector(final PluginBus bus, final String id)
    {
	super(bus, id);

	bus.registerPluginListener(new ConfigurationListener()
	{
	    public void setConfiguration(PluginConfig config)
	    {
		String url = config.getProperty("MudConnector", id, "listURL");
		if (url != null)
		{
		    try
		    {
			listURL = new URL(url);
		    }
		    catch (Exception e)
		    {
			MudConnector.this.error("" + e);
			errorLabel.setText("Error: " + e);
		    }
		}
		else
		{
		    MudConnector.this.error("no listURL specified");
		    errorLabel.setText("Missing list URL");
		    layouter.show(mudListPanel, "ERROR");
		}

		String sstep = config.getProperty("MudConnector", id, "step");

		try
		{
		    step = Integer.parseInt(sstep);
		}
		catch (Exception e)
		{
		    if (sstep != null)
			MudConnector.this.error("warning: " + sstep
				+ " is not a number");
		    step = 10;
		}
	    }
	});

	bus.registerPluginListener(new ReturnFocusListener()
	{
	    public void returnFocus()
	    {
		setup();
	    }
	});
	mudListPanel = new JPanel(layouter = new CardLayout())
	{
	    public void update(java.awt.Graphics g)
	    {
		paint(g);
	    }
	};

	mudListPanel.add("ERROR", errorLabel = new JLabel("Loading ..."));
	JPanel panel = new JPanel(new BorderLayout());
	panel.add("North", new JLabel("Loading mud list ... please wait"));
	panel.add("Center", progress = new ProgressBar());
	mudListPanel.add("PROGRESS", panel);
	panel = new JPanel(new BorderLayout());
	JScrollPane scrollPane = new JScrollPane(mudListSelector);
	panel.add("Center", scrollPane);
	mudListPanel.add("MUDLIST", panel);
	panel.add("East", panel = new JPanel(new GridLayout(3, 1)));
	panel.add(mudName = new JTextField(20));
	mudName.setEditable(false);
	JPanel apanel = new JPanel(new BorderLayout());
	apanel.add("Center", mudAddr = new JTextField(20));
	mudAddr.setEditable(false);
	apanel.add("East", mudPort = new JTextField(6));
	mudPort.setEditable(false);
	panel.add(apanel);
	panel.add(connect = new JButton("Connect"));

	connect.addActionListener(this);

	mudListSelector.setVisibleRowCount(3);
	mudListSelector.addListSelectionListener(new ListSelectionListener()
	{
	    public void valueChanged(ListSelectionEvent evt)
	    {
		JList list = (JList) evt.getSource();
		list.ensureIndexIsVisible(list.getSelectedIndex());
		String item = (String) list.getSelectedValue();
		mudName.setText(item);
		Object mud[] = (Object[]) mudList.get(item);
		mudAddr.setText((String) mud[0]);
		mudPort.setText(((Integer) mud[1]).toString());
	    }
	});

	layouter.show(mudListPanel, "PROGRESS");

	MCMenu = new JMenu("MudConnector");
    }

    private void setup()
    {
	if (mudList == null && listURL != null)
	    (new Thread(this)).start();
    }

    public void run()
    {
	try
	{

	    Map menuList = new HashMap();

	    mudList = new HashMap();
	    BufferedReader r = new BufferedReader(new InputStreamReader(listURL
		    .openStream()));

	    String line = r.readLine();
	    int mudCount = 0;
	    try
	    {
		mudCount = Integer.parseInt(line);
	    }
	    catch (NumberFormatException nfe)
	    {
		error("number of muds: " + nfe);
	    }
	    System.out.println("MudConnector: expecting " + mudCount
		    + " mud entries");
	    progress.setMax(mudCount);

	    StreamTokenizer ts = new StreamTokenizer(r);
	    ts.resetSyntax();
	    ts.whitespaceChars(0, 9);
	    ts.ordinaryChars(32, 255);
	    ts.wordChars(32, 255);

	    String name, host;
	    Integer port;
	    int token, counter = 0, idx = 0;

	    while ((token = ts.nextToken()) != ts.TT_EOF)
	    {
		name = ts.sval;

		if ((token = ts.nextToken()) != ts.TT_EOF)
		{
		    if (token == ts.TT_EOL)
			error(name + ": unexpected end of line"
				+ ", missing host and port");
		    host = ts.sval;
		    port = new Integer(23);
		    if ((token = ts.nextToken()) != ts.TT_EOF)
			try
			{
			    if (token == ts.TT_EOL)
				error(name + ": default port 23");
			    port = new Integer(ts.sval);
			}
			catch (NumberFormatException nfe)
			{
			    error("port for " + name + ": " + nfe);
			}

		    if (debug > 0)
			error(name + " [" + host + "," + port + "]");
		    mudList.put(name, new Object[]
		    { host, port, new Integer(idx++) });
		    progress.adjust(++counter, name);
		    mudListPanel.repaint();

		    String key = ("" + name.charAt(0)).toUpperCase();
		    JMenu subMenu = (JMenu) menuList.get(key);
		    if (subMenu == null)
		    {
			subMenu = new JMenu(key);
			MCMenu.add(subMenu);
			menuList.put(key, subMenu);
		    }
		    JMenuItem item = new JMenuItem(name);
		    item.addActionListener(MudConnector.this);
		    subMenu.add(item);
		}
		while (token != ts.TT_EOF && token != ts.TT_EOL)
		    token = ts.nextToken();
	    }
	    List list = new ArrayList(mudList.keySet());
	    Collections.sort(list);
	    mudListSelector.setListData(list.toArray());
	    System.out.println("MudConnector: found " + mudList.size()
		    + " entries");
	}
	catch (Exception e)
	{
	    error("error: " + e);
	    errorLabel.setText("Error: " + e);
	    layouter.show(mudListPanel, "ERROR");
	}
	layouter.show(mudListPanel, "MUDLIST");
    }

    public void actionPerformed(ActionEvent evt)
    {
	if (evt.getSource() instanceof MenuItem)
	{
	    String item = evt.getActionCommand();
	    int idx = ((Integer) ((Object[]) mudList.get(item))[2]).intValue();
	    mudListSelector.setSelectedIndex(idx);
	    mudName.setText(item);
	    Object mud[] = (Object[]) mudList.get(item);
	    mudAddr.setText((String) mud[0]);
	    mudPort.setText(((Integer) mud[1]).toString());
	}

	String addr = mudAddr.getText();
	String port = mudPort.getText();
	if (addr != null)
	{
	    bus.broadcast(new SocketRequest());
	    if (port == null || port.length() <= 0)
		port = "23";
	    bus.broadcast(new SocketRequest(addr, Integer.parseInt(port)));
	}
    }

    public JComponent getPluginVisual()
    {
	return mudListPanel;
    }

    public JMenu getPluginMenu()
    {
	return MCMenu;
    }
}
